package com.jk.vehicle.mini.controller;

import com.alibaba.excel.util.CollectionUtils;
import com.jk.vehicle.mini.config.MineConfig;
import com.jk.vehicle.mini.dto.MinioPreSignedDTO;
import com.jk.vehicle.mini.util.ContentTypeUtil;
import com.jk.vehicle.mini.util.RedisUtil;
import com.jk.vehicle.mini.vo.MinioPreSignedVO;
import com.jk.vehicle.core.controller.BaseController;
import com.jk.vehicle.core.entity.ResponseEntity;
import io.minio.*;
import io.minio.http.Method;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Api(value = "文件上传接口", tags = "文件上传接口")
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController extends BaseController {
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private MineConfig mineConfig;

    /**
     * 上传附件
     * @param saveFile 单个上传附件
     * @return 上传结果
     */
    @ApiOperation("单个文件上传")
    @ApiImplicitParam(name = "saveFile", value = "待上传文件")
    @PostMapping(value = "/upload")
    public ResponseEntity uploadAttachment(@RequestPart(value = "saveFile") MultipartFile saveFile){
        String fullFileName = saveFile.getOriginalFilename();	// 文件名
        if (StringUtils.isEmpty(fullFileName)){
            return failResult("文件名不能为空！");
        }
        String suffix = fullFileName.substring(fullFileName.indexOf("."));	// 获取文件后缀
        // 根据文件后缀名获取contentType
        String contentType = ContentTypeUtil.getContentType(suffix);
        // 使用MinIO服务的URL，端口，Access key和Secret key创建一个MinioClient对象
        MinioClient minioClient = new MinioClient.Builder().endpoint(mineConfig.minioEndpoint)
                .credentials(mineConfig.getMinioAccessKey(), mineConfig.getMinioSecretKey()).build();

        String objName;
        try {
            // 检查存储桶是否已经存在
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("chemical").build());
            // 如果不存在，则创建
            if (!found) {
                // 创建存储桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(mineConfig.getMinioDefaultBucket()).build());
            }

            // multipartFile转inputStream
            byte [] byteArr=saveFile.getBytes();
            InputStream inputStream = new ByteArrayInputStream(byteArr);

            // 文件名唯一处理
            objName = UUID.randomUUID().toString().replaceAll("-", "") + "_" + fullFileName;
            // 上传文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                    .bucket(mineConfig.getMinioDefaultBucket())
                    .object(objName)
                    .stream(inputStream, -1, 10485760)
                    .contentType(contentType)
                    .build());

        } catch (Exception e) {
            log.error("上传文件时出现异常：{}", e.getMessage());
            return failResult("上传文件出现异常：" + e.getMessage());
        }
        return successResult(objName);
    }

    /**
     * 获取文件http地址
     * @param minioPreSignedDTOList
     * @return 查询结果
     */
    @ApiOperation("获取文件http地址")
    @PostMapping(value = "/query_http_uri")
    public ResponseEntity<List<MinioPreSignedVO>> queryHttpUri(@RequestBody List<MinioPreSignedDTO> minioPreSignedDTOList)  {
        if (CollectionUtils.isEmpty(minioPreSignedDTOList)){
            return failResult("参数不可为空");
        }

        String url, redisKey;
        MinioClient minioClient;
        List<MinioPreSignedVO> minioPreSignedVOList = new ArrayList<>();
        for (MinioPreSignedDTO minioPreSignedDTO : minioPreSignedDTOList){
            // 如果没有填写，则用默认的存储桶
            if (StringUtils.isBlank(minioPreSignedDTO.getBucket())){
                minioPreSignedDTO.setBucket(mineConfig.getMinioDefaultBucket());
            }
            // 缓存的key采用 存储桶_对象名 的形式生成
            redisKey = minioPreSignedDTO.getBucket() + "_" + minioPreSignedDTO.getObjName();
            // 首先检查redis是否已经缓存,如果存在，直接获取，否则调用文件服务进行生成
            if (redisUtil.exists(redisKey)){
                minioPreSignedVOList.add(new MinioPreSignedVO(minioPreSignedDTO.getBucket(), minioPreSignedDTO.getObjName(),
                        redisUtil.get(redisKey) == null ? "" : redisUtil.get(redisKey).toString()));
                continue;
            }
            // 使用MinIO服务的URL，端口，Access key和Secret key创建一个MinioClient对象
            minioClient = new MinioClient.Builder()
                    .endpoint(mineConfig.minioEndpoint)
                    .credentials(mineConfig.getMinioAccessKey(), mineConfig.getMinioSecretKey())
                    .build();
            try {
                // 获取http访问路径，24小时有效期.
                url = minioClient.getPresignedObjectUrl(
                        GetPresignedObjectUrlArgs.builder()
                                .method(Method.GET)
                                .bucket(minioPreSignedDTO.getBucket())
                                .object(minioPreSignedDTO.getObjName())
                                .expiry(24, TimeUnit.HOURS)
                                .build());
                url = replaceDomainAndPort("www.szjktx.com/minio", true, null, url);
                // 存入redis缓存，并设置24小时有效期
                redisUtil.set(redisKey, url, 86400L);
                // 存入返回值
                minioPreSignedVOList.add(new MinioPreSignedVO(minioPreSignedDTO.getBucket(), minioPreSignedDTO.getObjName(), url));
            }catch (Exception e){
              log.error("获取文件http路径出现错误:{}, 参数为：{}", e.getMessage(), minioPreSignedDTO);
            }
        }

        return successResult(minioPreSignedVOList);
    }

    /**
     * 把url替换成域名
     * @param domain
     * @param bl
     * @param port
     * @param url
     * @return
     */
    public static String replaceDomainAndPort(String domain, Boolean bl, String port,String url) {
        String url_bak ="";
        if(url.indexOf("//") != -1){
            String[] splitTemp = url.split("//");
            if (bl) {
                url_bak = "https://";
            } else {
                url_bak = splitTemp[0]+"//";
            }
            if(port != null){
                url_bak = url_bak + domain+":"+port;
            } else {
                url_bak = url_bak + domain;
            }
            if(splitTemp.length >=1&& splitTemp[1].indexOf("/") != -1){
                String[] urlTemp2 = splitTemp[1].split("/");
                if(urlTemp2.length >1){
                    for(int i =1;i < urlTemp2.length; i++){
                        url_bak = url_bak +"/"+urlTemp2[i];
                    }
                }
            } else {

            }
        }
        return url_bak;
    }

    @ApiOperation("删除指定文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileNames", value = "待删除文件名称，多个逗号分隔"),
            @ApiImplicitParam(name = "bucket", value = "存储桶名称，不明白可以留空")
    })
    @GetMapping("/delete")
    public ResponseEntity deleteFile(String fileNames, String bucket){
        if (StringUtils.isBlank(fileNames)){
            return failResult("参数为空！");
        }
        if (StringUtils.isBlank(bucket)){
            bucket = mineConfig.minioDefaultBucket;
        }
        MinioClient minioClient = new MinioClient.Builder()
                .endpoint(mineConfig.minioEndpoint)
                .credentials(mineConfig.getMinioAccessKey(), mineConfig.getMinioSecretKey())
                .build();
        String[] fileNameArr = fileNames.split(",");
        for (String objName : fileNameArr){
            try{
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objName).build());
            }catch (Exception e){
                log.error("删除文件时出现异常：{}", e.getMessage());
            }
        }
        return successResult();
    }
}
